package com.zenith.storage.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.zenith.storage.mapper.StorageUploadChunkMapper;
import com.zenith.storage.mapper.StorageUploadSessionMapper;
import com.zenith.storage.po.domain.StorageUploadChunk;
import com.zenith.storage.po.domain.StorageUploadSession;
import com.zenith.storage.service.ChunkUploadService;
import com.zenith.storage.po.dto.ChunkCompleteDTO;
import com.zenith.storage.po.dto.ChunkInitDTO;
import com.zenith.storage.po.dto.ChunkUploadDTO;
import com.zenith.storage.po.vo.ChunkInitVO;
import com.zenith.storage.po.vo.ChunkStatusVO;
import com.zenith.storage.po.vo.ChunkUploadVO;
import com.zenith.storage.po.vo.FileVO;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.io.*;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.Comparator;
import java.util.List;
import java.util.stream.Collectors;

/**
 * 分片上传服务实现类
 */
@Slf4j
@Service
@RequiredArgsConstructor
public class ChunkUploadServiceImpl extends ServiceImpl<StorageUploadSessionMapper, StorageUploadSession> implements ChunkUploadService {
    
    private final StorageUploadSessionMapper storageUploadSessionMapper;
    private final StorageUploadChunkMapper storageUploadChunkMapper;
    
    // 本地存储路径配置
    @Value("${storage.local.path:/uploads}")
    private String localStoragePath;
    
    @Override
    @Transactional(rollbackFor = Exception.class)
    public ChunkInitVO initChunkUpload(ChunkInitDTO chunkInitDTO) {
        ChunkInitVO chunkInitVO = new ChunkInitVO();
        
        // 计算分片总数
        int chunkTotal = (int) Math.ceil((double) chunkInitDTO.getFileSize() / chunkInitDTO.getChunkSize());
        
        // 创建上传会话
        StorageUploadSession session = new StorageUploadSession();
        session.setUploadKey(chunkInitDTO.getUploadKey());
        session.setFileName(chunkInitDTO.getFileName());
        session.setFileSize(chunkInitDTO.getFileSize());
        session.setChunkSize(chunkInitDTO.getChunkSize());
        session.setChunkTotal(chunkTotal);
        session.setMd5(chunkInitDTO.getMd5());
        session.setStatus(0);
        storageUploadSessionMapper.insert(session);
        
        chunkInitVO.setSessionId(session.getId());
        chunkInitVO.setChunkTotal(chunkTotal);
        
        // 查询已上传的分片
        List<StorageUploadChunk> uploadedChunks = storageUploadChunkMapper.selectList(
                new LambdaQueryWrapper<StorageUploadChunk>()
                        .eq(StorageUploadChunk::getSessionId, session.getId())
                        .eq(StorageUploadChunk::getStatus, 1));
        
        chunkInitVO.setUploadedChunks(uploadedChunks.stream()
                .map(StorageUploadChunk::getChunkNo)
                .collect(Collectors.toList()));
        
        return chunkInitVO;
    }
    
    @Override
    @Transactional(rollbackFor = Exception.class)
    public ChunkUploadVO uploadChunk(ChunkUploadDTO chunkUploadDTO) {
        ChunkUploadVO chunkUploadVO = new ChunkUploadVO();
        chunkUploadVO.setChunkNo(chunkUploadDTO.getChunkNo());
        
        // 获取上传会话
        StorageUploadSession session = storageUploadSessionMapper.selectById(chunkUploadDTO.getSessionId());
        if (session == null) {
            throw new RuntimeException("上传会话不存在");
        }
        
        // 检查分片是否已存在
        StorageUploadChunk chunk = storageUploadChunkMapper.selectOne(
                new LambdaQueryWrapper<StorageUploadChunk>()
                        .eq(StorageUploadChunk::getSessionId, chunkUploadDTO.getSessionId())
                        .eq(StorageUploadChunk::getChunkNo, chunkUploadDTO.getChunkNo()));
        
        if (chunk == null) {
            // 创建分片记录
            chunk = new StorageUploadChunk();
            chunk.setSessionId(chunkUploadDTO.getSessionId());
            chunk.setChunkNo(chunkUploadDTO.getChunkNo());
            chunk.setStatus(1);
            
            storageUploadChunkMapper.insert(chunk);
        } else {
            // 更新分片状态
            chunk.setStatus(1);
            storageUploadChunkMapper.updateById(chunk);
        }
        
        // 更新会话状态为进行中
        if (session.getStatus() == 0) {
            session.setStatus(1);
            storageUploadSessionMapper.updateById(session);
        }
        
        chunkUploadVO.setIsComplete(false);
        
        // 检查是否所有分片都已上传
        List<StorageUploadChunk> uploadedChunks = storageUploadChunkMapper.selectList(
                new LambdaQueryWrapper<StorageUploadChunk>()
                        .eq(StorageUploadChunk::getSessionId, chunkUploadDTO.getSessionId())
                        .eq(StorageUploadChunk::getStatus, 1));
        
        if (uploadedChunks.size() == session.getChunkTotal()) {
            chunkUploadVO.setIsComplete(true);
        }
        
        return chunkUploadVO;
    }
    
    @Override
    @Transactional(rollbackFor = Exception.class)
    public FileVO completeChunkUpload(ChunkCompleteDTO chunkCompleteDTO) {
        // 获取上传会话
        StorageUploadSession session = storageUploadSessionMapper.selectById(chunkCompleteDTO.getSessionId());
        if (session == null) {
            throw new RuntimeException("上传会话不存在");
        }
        
        // 检查是否所有分片都已上传
        List<StorageUploadChunk> uploadedChunks = storageUploadChunkMapper.selectList(
                new LambdaQueryWrapper<StorageUploadChunk>()
                        .eq(StorageUploadChunk::getSessionId, chunkCompleteDTO.getSessionId())
                        .eq(StorageUploadChunk::getStatus, 1));
        
        if (uploadedChunks.size() != session.getChunkTotal()) {
            throw new RuntimeException("还有分片未上传完成");
        }
        
        // 更新会话状态为完成
        session.setStatus(2);
        storageUploadSessionMapper.updateById(session);
        
        // 合并分片文件到本地存储
        String mergedFilePath = mergeChunksLocally(session, uploadedChunks);
        
        // 创建文件记录
        FileVO fileVO = new FileVO();
        fileVO.setId(session.getId());
        fileVO.setFileName(session.getFileName());
        fileVO.setFileSize(session.getFileSize());
        fileVO.setFileKey(mergedFilePath);
        return fileVO;
    }
    
    /**
     * 在本地存储中合并分片文件
     * @param session 上传会话
     * @param uploadedChunks 已上传的分片列表
     * @return 合并后的文件路径
     */
    private String mergeChunksLocally(StorageUploadSession session, List<StorageUploadChunk> uploadedChunks) {
        // 确保本地存储目录存在
        Path storagePath = Paths.get(localStoragePath);
        try {
            Files.createDirectories(storagePath);
        } catch (IOException e) {
            log.error("创建本地存储目录失败: {}", e.getMessage(), e);
            throw new RuntimeException("创建本地存储目录失败", e);
        }
        
        // 构造合并后的文件路径
        String fileName = session.getId() + "_" + session.getFileName();
        Path mergedFilePath = storagePath.resolve(fileName);
        
        try (OutputStream outputStream = Files.newOutputStream(mergedFilePath)) {
            // 按顺序合并分片
            uploadedChunks.sort(Comparator.comparingInt(StorageUploadChunk::getChunkNo));
            
            for (StorageUploadChunk chunk : uploadedChunks) {
                // 构造分片文件路径（这里假设分片文件存储在临时目录中）
                Path chunkPath = storagePath.resolve("chunks").resolve(session.getId() + "_" + chunk.getChunkNo());
                
                // 如果分片文件存在，则将其内容追加到合并文件中
                if (Files.exists(chunkPath)) {
                    try (InputStream inputStream = Files.newInputStream(chunkPath)) {
                        byte[] buffer = new byte[8192];
                        int bytesRead;
                        while ((bytesRead = inputStream.read(buffer)) != -1) {
                            outputStream.write(buffer, 0, bytesRead);
                        }
                    }
                }
            }
            
            log.info("文件分片合并完成: {}", mergedFilePath.toString());
            return fileName;
        } catch (IOException e) {
            log.error("合并分片文件失败: {}", e.getMessage(), e);
            throw new RuntimeException("合并分片文件失败", e);
        }
    }
    
    @Override
    public ChunkStatusVO getChunkStatus(String uploadKey) {
        // 获取上传会话
        StorageUploadSession session = storageUploadSessionMapper.selectOne(
                new LambdaQueryWrapper<StorageUploadSession>()
                        .eq(StorageUploadSession::getUploadKey, uploadKey));
        
        if (session == null) {
            return null;
        }
        
        ChunkStatusVO chunkStatusVO = new ChunkStatusVO();
        chunkStatusVO.setSessionId(session.getId());
        chunkStatusVO.setFileName(session.getFileName());
        chunkStatusVO.setFileSize(session.getFileSize());
        chunkStatusVO.setChunkSize(session.getChunkSize());
        chunkStatusVO.setChunkTotal(session.getChunkTotal());
        chunkStatusVO.setStatus(session.getStatus());
        
        // 查询已上传的分片
        List<StorageUploadChunk> uploadedChunks = storageUploadChunkMapper.selectList(
                new LambdaQueryWrapper<StorageUploadChunk>()
                        .eq(StorageUploadChunk::getSessionId, session.getId())
                        .eq(StorageUploadChunk::getStatus, 1));
        
        chunkStatusVO.setUploadedChunks(uploadedChunks.stream()
                .map(StorageUploadChunk::getChunkNo)
                .collect(Collectors.toList()));
        
        return chunkStatusVO;
    }
}